package models;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

import javax.swing.table.DefaultTableModel;

import org.apache.log4j.Logger;

import util.ResultTable;
import util.SystemUtil;

import frame.FeatureHandler;


/**
 * @author Rishi Das Roy
 *
 * @Organization Institute Of Genomics & Integrative Biology
 */
public class Result {


	private ResultTable mRT;

	public Result(String output_file, FeatureHandler lfh, BaseModel model) throws IOException {

		Vector columnNames = new Vector();
		Vector data = new Vector();
		columnNames.addElement("Prediction");
		columnNames.addElement("Score");
		columnNames.addElement("prot_id");
		int i=0;
		mPosLabel = model.getPosLabel();
		mNegLabel = model.getNegLabel();
		BufferedReader lBR = new BufferedReader(new FileReader(output_file));
		mPredictedLabels = new ArrayList<Float>();
		String line=lBR.readLine();
		for (Integer actualLabel : lfh.getActualLabels()) {
			line=lBR.readLine();
			String[] scores = line.split("\\s");
			float label = Float.parseFloat(scores[0]);

			Float labelScore = Float.parseFloat(scores[1]);
			if(Math.abs(labelScore)<0.5)
				labelScore = 1 - labelScore;

			mPredictedLabels.add(label*labelScore);
			if(label*actualLabel>0){
				if(label>0){
					mTruePositive++;
				}else{
					mTrueNegative++;
				}

			}

			Vector row = new Vector();
			if(label>0)
				row.addElement(mPosLabel);
			else
				row.addElement(mNegLabel);

			row.addElement(SystemUtil.roundTwoDecimals(Math.abs(labelScore)));
			row.addElement(lfh.getId().get(i++));
			data.addElement(row);
		}

		mFalsePositive = lfh.getNegativeCounts() - mTrueNegative;
		mFalseNegative = lfh.getPositiveCounts() - mTruePositive;
		mTotal = lfh.getActualLabels().size();

		DefaultTableModel lTabModel = new DefaultTableModel(data, columnNames);
		mRT = new ResultTable(lTabModel);
		mRT.setName(model.getName());

	}

	public Result(List<Float> noProbResult, FeatureHandler lfh, BaseModel model) {

		Vector columnNames = new Vector();
		Vector data = new Vector();
		columnNames.addElement("Prot_id");
		columnNames.addElement("Prediction");


		mPosLabel = model.getPosLabel();
		mNegLabel = model.getNegLabel();

		mPredictedLabels = noProbResult;
		for (int i = 0; i < noProbResult.size(); i++) {
			Vector row = new Vector();
			row.addElement(lfh.getId().get(i));
			if(noProbResult.get(i)>0)
				row.addElement(mPosLabel);
			else
				row.addElement(mNegLabel);

			data.addElement(row);
		}

		mFalsePositive = lfh.getNegativeCounts() - mTrueNegative;
		mFalseNegative = lfh.getPositiveCounts() - mTruePositive;
		mTotal = lfh.getActualLabels().size();

		DefaultTableModel lTabModel = new DefaultTableModel(data, columnNames);
		mRT = new ResultTable(lTabModel);
		mRT.setName(model.getName());

	}

	private long mTruePositive = 0;
	private long mTrueNegative = 0;
	private List<Float> mPredictedLabels;
	private long mFalsePositive;
	private long mFalseNegative;
	private long mTotal;
	private String mPosLabel = "1";
	private String mNegLabel = "-1";

	private static Logger log = Logger.getLogger(Result.class);

	public long getFalseNegative() {
		return mFalseNegative;
	}

	public long getFalsePositive() {
		return mFalsePositive;
	}

	public List<Float> getPredictedLabels() {
		return mPredictedLabels;
	}

	public long getTrueNegative() {
		return mTrueNegative;
	}

	public long getTruePositive() {
		return mTruePositive;
	}

	public void writeResult(String pFilePath) throws IOException {
		BufferedWriter lBw = new BufferedWriter(new FileWriter(pFilePath));
		lBw.write("Confusion Matrix"+System.getProperty("line.separator"));
		lBw.write(",,Actual"+System.getProperty("line.separator"));
		lBw.write(",,"+mPosLabel+","+mNegLabel+",Total"+System.getProperty("line.separator"));
		lBw.write("Predicted,"+mPosLabel+","+mTruePositive+","+mFalsePositive+","+(mTruePositive+mFalsePositive)+System.getProperty("line.separator"));
		lBw.write("Predicted,"+mNegLabel+","+mFalseNegative+","+mTrueNegative+","+(mTrueNegative+mFalseNegative)+System.getProperty("line.separator"));
		lBw.write(",Total,"+(mTruePositive+mFalseNegative)+","+(mTrueNegative+mFalsePositive)+System.getProperty("line.separator"));

		lBw.write("Accuracy ,"+getAccuracy()+System.getProperty("line.separator"));
		lBw.write("Balance Accuracy ,"+getBAC()+System.getProperty("line.separator"));
		lBw.write("MCC ,"+getMcc()+System.getProperty("line.separator"));
		lBw.write("Class,"+mPosLabel+","+mNegLabel+System.getProperty("line.separator"));
		lBw.write("Recall ,"+getP_Recall()+","+getN_Recall()+System.getProperty("line.separator"));
		lBw.write("Precision ,"+getP_Precision()+","+getN_Precision()+System.getProperty("line.separator"));
		lBw.write("F-Measure ,"+getP_Fmeasure()+","+getN_Fmeasure()+System.getProperty("line.separator"));

		lBw.close();
	}

	private  float getBAC() {

		return (getP_Recall()+getN_Recall())/2;
	}

	private double getMcc() {
		double square_root = Math.sqrt((mTruePositive+mFalsePositive)*(mTruePositive+mFalseNegative)
					*(mTrueNegative+mFalsePositive)*(mTrueNegative+mFalseNegative));
		if(square_root<=0)
			return 0;


		return (mTruePositive*mTrueNegative - mFalsePositive*mFalseNegative)/square_root;
	}

	private float getAccuracy() {
		return (float)(mTruePositive+mTrueNegative)/mTotal;
	}

	private float getN_Fmeasure() {
		float denominator = (getN_Precision()+getN_Recall());
		if(denominator<=0)
			return 0;


		return ((float)2*getN_Precision()*getN_Recall()/denominator);
	}

	private float getP_Fmeasure() {
		float denominator = (getP_Precision()+getP_Recall());
		if(denominator<=0)
			return 0;


		return ((float)2*getP_Precision()*getP_Recall()/denominator);
	}

	private float getN_Precision() {
		long denominator = (mTrueNegative+mFalseNegative);
		if(denominator<=0)
			return 0;


		return ((float)mTrueNegative/denominator);
	}

	private float getP_Precision() {
		long denominator = (mTruePositive+mFalsePositive);
		if(denominator<=0)
			return 0;


		return ((float)mTruePositive/denominator);
	}

	private float getN_Recall() {
		long denominator = (mTrueNegative+mFalsePositive);
		if(denominator<=0)
			return 0;


		return ((float)mTrueNegative/denominator);
	}

	/**
	 * Sensitivity;Recall;
	 * true positive rate, hit rate
	 * @return
	 */
	private float getP_Recall() {
		long denominator = (mTruePositive+mFalseNegative);
		if(denominator<=0)
			return 0;

		return ((float)mTruePositive/denominator);
	}

	public ResultTable getMRT() {
		return mRT;
	}
}